package com.neusoft.elmboot.service.impl;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.List;

import javax.naming.InsufficientResourcesException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.neusoft.elmboot.mapper.VirtualWalletMapper;
import com.neusoft.elmboot.mapper.VirtualWalletTransactionMapper;
import com.neusoft.elmboot.po.VirtualWallet;
import com.neusoft.elmboot.po.VirtualWalletTransaction;
import com.neusoft.elmboot.service.VirtualWalletTransactionService;

@Service
public class VirtualWalletTransactionServiceImpl implements VirtualWalletTransactionService {
    @Autowired
    private  VirtualWalletTransactionMapper transactionMapper;
    @Autowired
    private VirtualWalletMapper virtualWalletMapper;
  
	@Override
	public int saveTransaction(VirtualWalletTransaction transaction) {
		return transactionMapper.saveTransaction(transaction);
	}

	@Override
	public List<VirtualWalletTransaction> listTransactionBywalletId(Integer walletId) {
		return transactionMapper.listTransactionBywalletId(walletId) ;
	}
	
	@Transactional
	@Override
	public int debit(VirtualWalletTransaction virtualWalletTransaction) {
		// TODO Auto-generated method stub
		virtualWalletTransaction.setCreateTime(new Timestamp(System.currentTimeMillis()));
		virtualWalletTransaction.setStatus("debit");
		virtualWalletTransaction.setToWalletId(virtualWalletTransaction.getToWalletId());
		VirtualWallet VirtualWallet = virtualWalletMapper.getVirtualWalletByWalletId(virtualWalletTransaction.getFromWalletId()); 
		VirtualWalletServ vwServ = new VirtualWalletServ(VirtualWallet.getWalletId(),
				VirtualWallet.getBalance(), VirtualWallet.getFrozenAmount(), VirtualWallet.getOverdraftLimit(),
				VirtualWallet.getUsedOverdraft(), VirtualWallet.getUserId());
		try {
			vwServ.debit(virtualWalletTransaction.getAmount(), new BigDecimal(0.1));
		} catch (InsufficientResourcesException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return 0;
		}
		transactionMapper.saveTransaction(virtualWalletTransaction);
		return virtualWalletMapper.updateBalance(vwServ.getWalletId(), vwServ.getBalance()) &
				virtualWalletMapper.updateUsedOverdraft(vwServ.getWalletId(), vwServ.getUsedOverdraft());
	}

	@Transactional
	@Override
	public int crebit(VirtualWalletTransaction virtualWalletTransaction) {
		// TODO Auto-generated method stub
		virtualWalletTransaction.setCreateTime(new Timestamp(System.currentTimeMillis()));
		virtualWalletTransaction.setStatus("credit");
		virtualWalletTransaction.setToWalletId(virtualWalletTransaction.getToWalletId());
		VirtualWallet virtualWallet = virtualWalletMapper.getVirtualWalletByWalletId(virtualWalletTransaction.getFromWalletId()); 
		VirtualWalletServ vwServ = new VirtualWalletServ(virtualWallet.getWalletId(),
				virtualWallet.getBalance(), virtualWallet.getUserId());
		try {
			vwServ.credit(virtualWalletTransaction.getAmount());
		} catch (InsufficientResourcesException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return 0;
		}
		transactionMapper.saveTransaction(virtualWalletTransaction);
		return virtualWalletMapper.updateBalance(vwServ.getWalletId(), vwServ.getBalance());
	}

	@Transactional
	@Override
	public int pay(VirtualWalletTransaction virtualWalletTransaction) {
		// TODO Auto-generated method stub
		virtualWalletTransaction.setCreateTime(new Timestamp(System.currentTimeMillis()));
		virtualWalletTransaction.setStatus("pay");
		VirtualWallet fromVirtualWallet = virtualWalletMapper.getVirtualWalletByWalletId(virtualWalletTransaction.getFromWalletId());
		VirtualWallet toVirtualWallet = virtualWalletMapper.getVirtualWalletByWalletId(virtualWalletTransaction.getToWalletId());
		VirtualWalletServ fromvwServ = new VirtualWalletServ(fromVirtualWallet.getWalletId(),
				fromVirtualWallet.getBalance(), fromVirtualWallet.getUserId());
		VirtualWalletServ tovwServ = new VirtualWalletServ(toVirtualWallet.getWalletId(),
				toVirtualWallet.getBalance(), toVirtualWallet.getUserId());
		try {
			fromvwServ.payOut(virtualWalletTransaction.getAmount());
			tovwServ.payIn(virtualWalletTransaction.getAmount());
		} catch (InsufficientResourcesException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return 0;
		}
		transactionMapper.saveTransaction(virtualWalletTransaction);
		return virtualWalletMapper.updateBalance(fromvwServ.getWalletId(), fromvwServ.getBalance()) & virtualWalletMapper.updateBalance(tovwServ.getWalletId(), tovwServ.getBalance());
	}
	
	@Transactional
	@Override
	public int frozen(VirtualWalletTransaction virtualWalletTransaction) {
		// TODO Auto-generated method stub
		virtualWalletTransaction.setCreateTime(new Timestamp(System.currentTimeMillis()));
		virtualWalletTransaction.setStatus("frozen");
		virtualWalletTransaction.setToWalletId(virtualWalletTransaction.getToWalletId());
		VirtualWallet VirtualWallet = virtualWalletMapper.getVirtualWalletByWalletId(virtualWalletTransaction.getFromWalletId());
		VirtualWalletServ vwServ = new VirtualWalletServ(VirtualWallet.getWalletId(),
				VirtualWallet.getBalance(), VirtualWallet.getFrozenAmount(), VirtualWallet.getOverdraftLimit(),
				VirtualWallet.getUsedOverdraft(), VirtualWallet.getUserId());
		try {
			vwServ.frozen(virtualWalletTransaction.getAmount());
		} catch (InsufficientResourcesException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return 0;
		}
		transactionMapper.saveTransaction(virtualWalletTransaction);
		return virtualWalletMapper.updateFrozenAmount(vwServ.getWalletId(), vwServ.getFrozenAmount());
	}
	
	@Transactional
	@Override
	public int unfrozen(VirtualWalletTransaction virtualWalletTransaction) {
		// TODO Auto-generated method stub
		virtualWalletTransaction.setCreateTime(new Timestamp(System.currentTimeMillis()));
		virtualWalletTransaction.setStatus("unfrozen");
		virtualWalletTransaction.setToWalletId(virtualWalletTransaction.getToWalletId());
		VirtualWallet VirtualWallet = virtualWalletMapper.getVirtualWalletByWalletId(virtualWalletTransaction.getFromWalletId());
		VirtualWalletServ vwServ = new VirtualWalletServ(VirtualWallet.getWalletId(),
				VirtualWallet.getBalance(), VirtualWallet.getFrozenAmount(), VirtualWallet.getOverdraftLimit(),
				VirtualWallet.getUsedOverdraft(), VirtualWallet.getUserId());
		try {
			vwServ.unfrozen(virtualWalletTransaction.getAmount());
		} catch (InsufficientResourcesException e) {
			e.printStackTrace();
			return 0;
		}
		transactionMapper.saveTransaction(virtualWalletTransaction);
		return virtualWalletMapper.updateFrozenAmount(vwServ.getWalletId(), vwServ.getFrozenAmount()) & 
				virtualWalletMapper.updateBalance(vwServ.getWalletId(), vwServ.getBalance());
	}

	@Transactional
	@Override
	public int payOverdraft(VirtualWalletTransaction virtualWalletTransaction) {
		virtualWalletTransaction.setCreateTime(new Timestamp(System.currentTimeMillis()));
		virtualWalletTransaction.setStatus("payOverdraft");
		virtualWalletTransaction.setToWalletId(virtualWalletTransaction.getToWalletId());
		VirtualWallet VirtualWallet = virtualWalletMapper.getVirtualWalletByWalletId(virtualWalletTransaction.getFromWalletId());
		VirtualWalletServ vwServ = new VirtualWalletServ(VirtualWallet.getWalletId(),
				VirtualWallet.getBalance(), VirtualWallet.getFrozenAmount(), VirtualWallet.getOverdraftLimit(),
				VirtualWallet.getUsedOverdraft(), VirtualWallet.getUserId());
		try {
			vwServ.payOverdraft(virtualWalletTransaction.getAmount());
		} catch (InsufficientResourcesException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return 0;
		}
		transactionMapper.saveTransaction(virtualWalletTransaction);
		return virtualWalletMapper.updateBalance(vwServ.getWalletId(), vwServ.getBalance()) &
				virtualWalletMapper.updateUsedOverdraft(vwServ.getWalletId(), vwServ.getUsedOverdraft());
	}

	@Scheduled(cron = "0 0 0 * * ?")
	@Transactional
	@Override
	public void checkOverdraft() {
		BigDecimal interestRate = new BigDecimal(0.01);
		List<VirtualWallet> virtualWallets = virtualWalletMapper.getVirtualWalletList();
		for(VirtualWallet virtualWallet : virtualWallets) {
			VirtualWalletServ vwServ = new VirtualWalletServ(virtualWallet.getWalletId(),
					virtualWallet.getBalance(), virtualWallet.getFrozenAmount(), virtualWallet.getOverdraftLimit(),
					virtualWallet.getUsedOverdraft(), virtualWallet.getUserId());
			if(vwServ.getUsedOverdraft().compareTo(BigDecimal.ZERO) == 0) {
				continue;
			}
			
			BigDecimal interest = vwServ.checkOverdraft(interestRate);
			
			virtualWalletMapper.updateBalance(vwServ.getWalletId(), vwServ.getBalance());
			virtualWalletMapper.updateUsedOverdraft(vwServ.getWalletId(), vwServ.getUsedOverdraft());
			VirtualWalletTransaction virtualWalletTransaction = new VirtualWalletTransaction(new Timestamp(System.currentTimeMillis()),
					interest, "interestOfOverdraft", vwServ.getWalletId(), vwServ.getWalletId());
			transactionMapper.saveTransaction(virtualWalletTransaction);
		}
		
	}

}


class VirtualWalletServ {
	private Integer walletId;
	private BigDecimal balance;
	private BigDecimal frozenAmount;
	private BigDecimal overdraftLimit;
	private BigDecimal usedOverdraft;
    private String userId;
    public VirtualWalletServ(Integer walletId, BigDecimal balance, String userId) {
    	this.walletId = walletId;
    	this.balance = balance;
    	this.userId = userId;
    }
    public VirtualWalletServ(Integer walletId, BigDecimal balance, BigDecimal frozenAmount, BigDecimal overdraftLimit, BigDecimal usedOverdraft, String userId) {
    	this.walletId = walletId;
    	this.balance = balance;
    	this.frozenAmount = frozenAmount;
    	this.overdraftLimit = overdraftLimit;
    	this.usedOverdraft = usedOverdraft;
    	this.userId = userId;
    }
    public void debit(BigDecimal amount, BigDecimal debitRate) throws InsufficientResourcesException {
    	debitWithOverdraft(amount, debitRate);
    }
    public void credit(BigDecimal amount) throws InsufficientResourcesException {
    	if(amount.compareTo(BigDecimal.ZERO)<0) {
    		throw new InsufficientResourcesException();
    	}
    	BigDecimal bonus = amount.divide(new BigDecimal(1000)).multiply(new BigDecimal(100));
    	this.balance = this.balance.add(amount).add(bonus);
    }
    public void payOut(BigDecimal amount) throws InsufficientResourcesException {
//    	if(this.balance.compareTo(amount)<0) {
//    		throw new InsufficientResourcesException();
//    	}
//    	this.balance = this.balance.subtract(amount);
    	debitWithOverdraft(amount, BigDecimal.ZERO);
    }
    public void payIn(BigDecimal amount) throws InsufficientResourcesException {
    	if(amount.compareTo(BigDecimal.ZERO) < 0) {
    		throw new InsufficientResourcesException();
    	}
    	this.balance = this.balance.add(amount);
    }
    public void frozen(BigDecimal amount) throws InsufficientResourcesException {
    	if(amount.compareTo(BigDecimal.ZERO) < 0) {
    		throw new InsufficientResourcesException();
    	}
    	this.frozenAmount = this.frozenAmount.add(amount);
	}
    public void unfrozen(BigDecimal amount) throws InsufficientResourcesException {
    	if(this.frozenAmount.compareTo(amount) < 0) {
    		throw new InsufficientResourcesException();
    	}
    	this.frozenAmount = this.frozenAmount.subtract(amount);
    	this.balance = this.balance.add(amount);
	}
    //出账的钱和需要支付的手续费占比
    public void debitWithOverdraft(BigDecimal amount,BigDecimal deducationNum) throws InsufficientResourcesException {
    	//扣除
    	BigDecimal deduction = amount.multiply(deducationNum);
    	BigDecimal availableBalance = this.balance.add(this.overdraftLimit.subtract(usedOverdraft));
    	if(availableBalance.compareTo(amount)<0) {
    		throw new InsufficientResourcesException();
    	}
    	else if(this.balance.compareTo(amount.add(deduction)) >= 0) {
    		this.balance = this.balance.subtract(amount).subtract(deduction);
    		return;
    	}
    	else {
			BigDecimal overdraftAmount = amount.add(deduction).subtract(this.balance);
			this.balance = BigDecimal.ZERO;
			this.usedOverdraft = this.usedOverdraft.add(overdraftAmount);
		}
    	if(this.overdraftLimit.compareTo(this.usedOverdraft) < 0) {
    		throw new InsufficientResourcesException();
    	}
    }
    public void payOverdraft(BigDecimal amount) throws InsufficientResourcesException {
    	if(amount.compareTo(this.usedOverdraft)>=0) {
    		amount = this.usedOverdraft;
    		this.usedOverdraft = BigDecimal.ZERO;
    	}
    	else {
    		this.usedOverdraft = this.usedOverdraft.subtract(amount);
    	}
    	if(this.balance.compareTo(amount)<0) {
    		throw new InsufficientResourcesException();
    	}
    	this.balance = this.balance.subtract(amount);
    }
    public BigDecimal checkOverdraft(BigDecimal interestRate) {
		BigDecimal interest = this.usedOverdraft.multiply(interestRate);
		if(interest.compareTo(new BigDecimal(0.01))<0) {
			interest = new BigDecimal(0.01);
		}
		if(this.balance.compareTo(interest) > 0) {
			this.balance = this.balance.subtract(interest);
		}
		else {
			BigDecimal tmp = interest.subtract(this.balance);
			this.balance = BigDecimal.ZERO;
			this.usedOverdraft = this.usedOverdraft.add(tmp);
		}
		return interest;
	}
	public Integer getWalletId() {
		return walletId;
	}
	public void setWalletId(Integer walletId) {
		this.walletId = walletId;
	}
	public BigDecimal getBalance() {
		return balance;
	}
	public void setBalance(BigDecimal balance) {
		this.balance = balance;
	}
	public String getUserId() {
		return userId;
	}
	public void setUserId(String userId) {
		this.userId = userId;
	}
	public BigDecimal getFrozenAmount() {
		return frozenAmount;
	}
	public void setFrozenAmount(BigDecimal frozenAmount) {
		this.frozenAmount = frozenAmount;
	}
	public BigDecimal getOverdraftLimit() {
		return overdraftLimit;
	}
	public void setOverdraftLimit(BigDecimal overdraftLimit) {
		this.overdraftLimit = overdraftLimit;
	}
	public BigDecimal getUsedOverdraft() {
		return usedOverdraft;
	}
	public void setUsedOverdraft(BigDecimal usedOverdraft) {
		this.usedOverdraft = usedOverdraft;
	}
	
}